﻿
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Highlighting.Xshd;
using ICSharpCode.AvalonEdit.Rendering;
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;


namespace BoilenEditor.Primitives {

    public partial class BindableTextEditor : TextEditor {

        private static readonly IHighlightingDefinition BoilenHighlighting = LoadBoilenHighlighting( );


        public BindableTextEditor( ) {
            this.SyntaxHighlighting = BoilenHighlighting;

            TextEditorOptions options = this.Options;
            options.AllowScrollBelowDocument = true;
            options.ConvertTabsToSpaces = true;

            var commandBindings = this.TextArea.CommandBindings;
            commandBindings.Add( new CommandBinding( ComponentCommands.MoveUp, this.MoveUpExecuted ) );
            commandBindings.Add( new CommandBinding( ComponentCommands.MoveDown, this.MoveDownExecuted, this.MoveDownCanExecute ) );

            var inputBindings = this.TextArea.InputBindings;
            inputBindings.Add( new KeyBinding( ComponentCommands.MoveUp, Key.Up, ModifierKeys.Control ) );
            inputBindings.Add( new KeyBinding( ComponentCommands.MoveDown, Key.Down, ModifierKeys.Control ) );
            inputBindings.Add( new KeyBinding( ApplicationCommands.Redo, Key.Z, ModifierKeys.Control | ModifierKeys.Shift ) );
        }


        public string GetLineText( int lineIndex ) {
            DocumentLine line = this.Document.Lines[lineIndex];
            string text = this.Document.GetText( line );
            return text;
        }

        public int GetCharacterIndexFromLineIndex( int lineIndex ) {
            DocumentLine line = this.Document.Lines[lineIndex];
            return line.Offset;
        }

        public int GetLineIndexFromCharacterIndex( int charIndex ) {
            DocumentLine line = this.Document.GetLineByOffset( charIndex );
            return line.LineNumber - 1;
        }


        public override void OnApplyTemplate( ) {
            base.OnApplyTemplate( );

            ScrollViewer scrollViewer = Template.FindName( "PART_ScrollViewer", this ) as ScrollViewer;
            if( scrollViewer.HasValue( ) )
                scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
        }


        private static object BindableTextCoerce( DependencyObject d, object value ) {
            return value ?? "";
        }

        protected override void OnTextChanged( EventArgs e ) {
            base.OnTextChanged( e );

            if( !this.IsReadOnly )
                this.BindableText = this.Text;
        }

        private void OnBindableTextChanged( ) {
            string text = this.BindableText;
            if( this.Text != text )
                this.Text = text;
        }


        private void MoveDownCanExecute( object sender, CanExecuteRoutedEventArgs e ) {
            TextView textView = this.TextArea.TextView;
            VisualLine lastVisualLine = textView.VisualLines.Last( );
            DocumentLine lastVisibleLine = lastVisualLine.LastDocumentLine;
            DocumentLine lastDocumentLine = this.Document.Lines.Last( );

            if( textView.VerticalOffset != 0.0 || lastVisibleLine != lastDocumentLine )
                e.CanExecute = true;
        }

        private void MoveDownExecuted( object sender, ExecutedRoutedEventArgs e ) {
            this.LineDown( );
        }

        private void MoveUpExecuted( object sender, ExecutedRoutedEventArgs e ) {
            this.LineUp( );
        }


        private static IHighlightingDefinition LoadBoilenHighlighting( ) {
            const string BoilenHighlightingResource = "BoilenEditor.Properties.Boilen.xshd";

            using( var stream = typeof( BindableTextEditor ).Assembly.GetManifestResourceStream( BoilenHighlightingResource ) )
            using( var reader = new System.Xml.XmlTextReader( stream ) ) {
                XshdSyntaxDefinition xshd = HighlightingLoader.LoadXshd( reader );
                IHighlightingDefinition highlighting = HighlightingLoader.Load( xshd, null );
                return highlighting;
            }
        }

    }

}
